﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Reflection;

namespace Mr.LinqToWeibo
{
	internal class ExpressionTreeHelpers
	{
		internal static bool IsMemberEqualsValueExpression (Expression exp, Type declaringType, string memberName)
		{
			if (exp.NodeType != ExpressionType.Equal)
				return false;

			BinaryExpression be = (BinaryExpression)exp;

			// Assert.
			if (ExpressionTreeHelpers.IsSpecificMemberExpression (be.Left, declaringType, memberName) &&
                ExpressionTreeHelpers.IsSpecificMemberExpression (be.Right, declaringType, memberName))
				throw new Exception ("Cannot have 'member' == 'member' in an expression!");

			return (ExpressionTreeHelpers.IsSpecificMemberExpression (be.Left, declaringType, memberName) ||
                ExpressionTreeHelpers.IsSpecificMemberExpression (be.Right, declaringType, memberName));
		}

		internal static bool IsSpecificMemberExpression (Expression exp, Type declaringType, string memberName)
		{
			return ((exp is MemberExpression) &&
                (((MemberExpression)exp).Member.DeclaringType == declaringType) &&
                (((MemberExpression)exp).Member.Name == memberName));
		}

		internal static string GetValueFromEqualsExpression (BinaryExpression be, Type memberDeclaringType, string memberName)
		{
			if (be.NodeType != ExpressionType.Equal)
				throw new Exception ("There is a bug in this program.");

			if (be.Left.NodeType == ExpressionType.MemberAccess) {
				MemberExpression me = (MemberExpression)be.Left;

				if (me.Member.DeclaringType == memberDeclaringType && me.Member.Name == memberName) {
					return GetValueFromExpression (be.Right);
				}
			} else if (be.Right.NodeType == ExpressionType.MemberAccess) {
				MemberExpression me = (MemberExpression)be.Right;

				if (me.Member.DeclaringType == memberDeclaringType && me.Member.Name == memberName) {
					return GetValueFromExpression (be.Left);
				}
			}

			// We should have returned by now.
			throw new Exception ("There is a bug in this program.");
		}

		internal static string GetValueFromExpression (Expression expression)
		{
			if (expression.NodeType == ExpressionType.Constant)
				return (string)(((ConstantExpression)expression).Value);
			else
				throw new InvalidProgramException (
                    String.Format ("The expression type {0} is not supported to obtain a value.", expression.NodeType));
		}

		internal static object GetValFromExpression (Expression expression)
		{
			if (expression is ConstantExpression) {
				return ((ConstantExpression)expression).Value;
			} else if (expression is MemberExpression) {
				Expression deep = expression;
				Stack<string > properties = new Stack<string> ();
				
				while (deep is MemberExpression) {
					properties.Push (((MemberExpression)deep).Member.Name);
					deep = ((MemberExpression)deep).Expression;
				}
				
				if (deep is ConstantExpression) {
					ConstantExpression ce = deep as ConstantExpression;
					object val = ce.Value;
					foreach (string p in properties) {
						PropertyInfo pi = val.GetType ().GetProperty (p);
						if (pi != null) {
							val = pi.GetValue (val, null);
						} else {
							FieldInfo fi = val.GetType ().GetField (p);
							if (fi != null) {
								val = fi.GetValue (val);
							}
						}
					}
					return val;
				}
				
				return null;
				
			} else if (expression is BinaryExpression) {
				BinaryExpression exp = expression as BinaryExpression;
				return GetValFromExpression (exp.Left) ?? GetValFromExpression (exp.Right);
			}
			return null;
		}
		
		internal static MemberExpression GetRootParameterMemberExpression (BinaryExpression be)
		{
			if (be.Left is MemberExpression && !CheckRootIfConstant ((MemberExpression)(be.Left))) {
				return be.Left as MemberExpression;
			} else if (be.Right is MemberExpression && !CheckRootIfConstant ((MemberExpression)(be.Right))) {
				return be.Right as MemberExpression;
			} else if (be.Left is UnaryExpression) {
				Expression e = ((UnaryExpression)(be.Left)).Operand;
				if (e is MemberExpression) {
					return e as MemberExpression;
				}
			} else if (be.Right is UnaryExpression) {
				Expression e = ((UnaryExpression)(be.Right)).Operand;
				if (e is MemberExpression) {
					return e as MemberExpression;
				}
			}
			return null;
		}
		
		private static bool CheckRootIfConstant (MemberExpression me)
		{
			Expression deep = me;
			while (deep is MemberExpression) {
				deep = ((MemberExpression)deep).Expression;
			}
			
			if (deep is ConstantExpression) {
				return true;
			} else
				return false;
		}
	}
}
